﻿/*
____
 L|ypps Web controls for ASP.NET

Copyright (c) 2009 Alessandro Zifiglio, http://www.Typps.com
EMail: alessandro@Typps.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Typps
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Configuration;
    using System.Data;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Drawing.Drawing2D;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Resources;
    using System.Text;
    using System.Threading;
    using System.Web;
    using System.Web.Hosting;
    using System.Web.Script.Serialization;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml;

    [ToolboxBitmap(typeof(ResourceDummy), "Typps.Resources.Images.imagepicker.bmp")]
    [DefaultProperty("TargetControlImageUrl")]
    [Designer(typeof(Typps.ImagePickerDesigner))]
    [ToolboxData("<{0}:ImagePicker runat=\"server\"></{0}:ImagePicker>")]
    public class ImagePicker : ControlBase
    {
        #region Fields

        string[] _commonCssImageList = new string[] { "tristatebutton80x33.gif", "tristatebutton110x33.gif", "tristatebutton200x33.gif", "tristateclosebutton16x48.gif" };
        ClientScriptManager _cs;
        string _cssKey = "Typps.ImagePicker";
        MediaFrame _embeddedMediaFrame;
        UpdatePanelProgress _embeddedUpdatePanelProgress;
        bool _enumerateGallery = false;
        HTreeNode _imageGalleryNode;
        string _keyFormat = "ImagePicker_{0}";
        System.Web.UI.WebControls.Image _popperImage = null;
        string _rootImageSkinFolder = "Typps.Resources.Skins.{0}.Images.ImagePicker.{1}";
        string _rootSkinFolder = "Typps.Resources.Skins.{0}.{1}";
        string _rootTemplateSkinFolder = "Typps.Resources.Skins.{0}.Templates.{1}";
        string _styleSheetName = "Css.imagepicker.css";
        Color _updateProgressBackgroundColor = Color.Black;
        int _updateProgressTransparency = 20;

        //Invoke delegates registered with the Click event.
        private string _webResponseData = string.Empty;

        #endregion Fields

        #region Constructors

        public ImagePicker()
            : base("ul")
        {
        }

        #endregion Constructors

        #region Events

        public event EventHandler BrowseServerRequest;

        public event ImageEventHandler ImagePropertiesChanged;

        public event UploadedImageFileEventHandler ImageUploaded;

        public event SelectedImageEventHandler SelectedImageChanged;

        #endregion Events

        #region Properties

        /// <summary>
        /// Compositing is done during rendering when the source pixels are combined with the destination pixels to produce the resultant pixels. 
        /// The quality of compositing directly relates to the visual quality of the output and is inversely proportional to the render time. 
        /// The higher the quality, the slower the render time. This is because the higher the quality level, the more surrounding pixels need to be taken into 
        /// account during the composite. The linear quality setting (AssumeLinear) compromises by providing better quality than the 
        /// default quality at a slightly lower speed.
        /// </summary>
        [DefaultValue(typeof(CompositingQuality), "Default")]
        public CompositingQuality ComposititionQuality
        {
            get
            {
                return (CompositingQuality)(ViewState["CompositingQuality"] ?? CompositingQuality.Default);
            }
            set
            {
                ViewState["CompositingQuality"] = value;
            }
        }

        [DefaultValue(true),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool DisplayImageDesignerAttributes
        {
            get { return (bool)(ViewState["DisplayImageDesignerAttributes"] ?? true); }
            set { ViewState["DisplayImageDesignerAttributes"] = value; }
        }

        /// <summary>
        /// Gets/Sets a value on whether the ImageUrl textbox should be shown when both 
        /// ImageUploadEnabled and DisplayImageDesignerAttributes are set to false. In this case
        /// the ImageUrlTextBox will not show which can be forced to display via this property setting.
        /// </summary>
        [DefaultValue(true),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool DisplayImageDesignerUrlField
        {
            get { return (bool)(ViewState["DisplayImageDesignerUrlField"] ?? true); }
            set { ViewState["DisplayImageDesignerUrlField"] = value; }
        }

        [PersistenceMode(PersistenceMode.InnerProperty),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public MediaFrame EmbeddedMediaFrame
        {
            get
            {
                if (_embeddedMediaFrame == null)
                    _embeddedMediaFrame = new MediaFrame();
                return _embeddedMediaFrame;
            }
        }

        [PersistenceMode(PersistenceMode.InnerProperty),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public UpdatePanelProgress EmbeddedUpdatePanelProgress
        {
            get
            {
                if (_embeddedUpdatePanelProgress == null)
                    _embeddedUpdatePanelProgress = new UpdatePanelProgress();
                return _embeddedUpdatePanelProgress;
            }
        }

        [DefaultValue(true),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool EnableMediaFrame
        {
            get
            {
                return (ViewState["EnableMediaFrame"] == null) ?
                    true : (bool)ViewState["EnableMediaFrame"];
            }
            set
            {
                ViewState["EnableMediaFrame"] = value;
            }
        }

        [DefaultValue(true),
        Description("Enable/Disable Image browser"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool ImageBrowserEnabled
        {
            get { return (ViewState["ImageBrowserEnabled"] == null) ? true : (bool)ViewState["ImageBrowserEnabled"]; }
            set { ViewState["ImageBrowserEnabled"] = value; }
        }

        [DefaultValue("Loading image browser..."),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string ImageBrowserLoadingMessage
        {
            get { return (ViewState["ImageBrowserLoadingMessage"] == null) ? GetLocalizedText("ImageBrowserLoadingMessage") : ViewState["ImageBrowserLoadingMessage"].ToString(); }
            set { ViewState["ImageBrowserLoadingMessage"] = value; }
        }

        [DefaultValue("Loading image designer..."),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string ImageDesignerLoadingMessage
        {
            get { return (ViewState["ImageDesignerLoadingMessage"] == null) ? GetLocalizedText("ImageDesignerLoadingMessage") : ViewState["ImageDesignerLoadingMessage"].ToString(); }
            set { ViewState["ImageDesignerLoadingMessage"] = value; }
        }

        [Browsable(false)]
        public HTreeNode ImageGalleryNode
        {
            get
            {
                if (_imageGalleryNode == null)
                    _imageGalleryNode = new HTreeNode();
                return _imageGalleryNode;
            }
        }

        [DefaultValue("Loading image uploader..."),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string ImageUploadDesignerLoadingMessage
        {
            get { return (ViewState["ImageUploadDesignerLoadingMessage"] == null) ? GetLocalizedText("ImageUploadDesignerLoadingMessage") : ViewState["ImageUploadDesignerLoadingMessage"].ToString(); }
            set { ViewState["ImageUploadDesignerLoadingMessage"] = value; }
        }

        [DefaultValue(true),
        Description("Enable/Disable Image Upload"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool ImageUploadEnabled
        {
            get { return (ViewState["ImageUploadEnabled"] == null) ? true : (bool)ViewState["ImageUploadEnabled"]; }
            set { ViewState["ImageUploadEnabled"] = value; }
        }

        [DefaultValue("Image upload in progress..."),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string ImageUploadingMessage
        {
            get { return (ViewState["ImageUploadingMessage"] == null) ? GetLocalizedText("ImageUploadingMessage") : ViewState["ImageUploadingMessage"].ToString(); }
            set { ViewState["ImageUploadingMessage"] = value; }
        }

        /// <summary>
        /// Gets/Sets the InterpolationMode of the resized image displayed in the image browser. 
        /// The interpolation mode of a Graphics object influences the way GDI+ scales (stretches and shrinks) image. 
        /// Reference msdn for details on this property.
        /// </summary>
        [DefaultValue(typeof(InterpolationMode), "Default")]
        public InterpolationMode InterpolationMode
        {
            get
            {
                return (InterpolationMode)(ViewState["InterpolationMode"] ?? InterpolationMode.Default);
            }
            set
            {
                ViewState["InterpolationMode"] = value;
            }
        }

        [Browsable(false)]
        public override bool IsCallback
        {
            get
            {
                if (DesignMode || Context == null) return false;
                string key = string.Format("iscallback${0}", UniqueID);
                string value = Context.Request.Form[key];
                if (!string.IsNullOrEmpty(value))
                    return true;
                return IsFileUploading;
            }
        }

        [Browsable(false)]
        public bool IsFileUploading
        {
            get
            {
                string value = Context.Request.Form[Page.postEventSourceID];
                string id = this.ClientID + "_fileupload";
                if (!string.IsNullOrEmpty(value))
                    if (value == id)
                        return true;
                return false;
            }
        }

        /// <summary>
        /// The url to a default media to display in case the original intended media load fails.
        /// </summary>
        [DefaultValue("")]
        [Editor(typeof(System.Web.UI.Design.UrlEditor), typeof(UITypeEditor)),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string LoadFailureMediaUrl
        {
            get
            {
                return (string)(ViewState["LoadFailureMediaUrl"] ?? string.Empty);
            }
            set
            {
                ViewState["LoadFailureMediaUrl"] = value;
            }
        }

        /// <summary>
        /// Control is initialized manually by a call clientside via
        /// initializeRequest(). In order to do this, you need to use
        /// $find() first and then call the initializeRequest method on it.
        /// </summary>
        [DefaultValue(false),
        Category("Control will be initialized manually"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public bool ManuallyInitialize
        {
            get { return (ViewState["ManuallyInitialize"] == null) ? false : (bool)ViewState["ManuallyInitialize"]; }
            set { ViewState["ManuallyInitialize"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientBeginWebRequest
        {
            get { return (ViewState["OnClientBeginWebRequest"] == null) ? string.Empty : (string)ViewState["OnClientBeginWebRequest"]; }
            set { ViewState["OnClientBeginWebRequest"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientCancelButton
        {
            get { return (ViewState["OnClientCancelButton"] == null) ? string.Empty : (string)ViewState["OnClientCancelButton"]; }
            set { ViewState["OnClientCancelButton"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientEndWebRequest
        {
            get { return (ViewState["OnClientEndWebRequest"] == null) ? string.Empty : (string)ViewState["OnClientEndWebRequest"]; }
            set { ViewState["OnClientEndWebRequest"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientOkButton
        {
            get { return (ViewState["OnClientOkButton"] == null) ? string.Empty : (string)ViewState["OnClientOkButton"]; }
            set { ViewState["OnClientOkButton"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientTargetControlAction
        {
            get { return (ViewState["OnClientTargetControlAction"] == null) ? string.Empty : (string)ViewState["OnClientTargetControlAction"]; }
            set { ViewState["OnClientTargetControlAction"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientThumbnailClicked
        {
            get { return (ViewState["OnClientThumbnailClicked"] == null) ? string.Empty : (string)ViewState["OnClientThumbnailClicked"]; }
            set { ViewState["OnClientThumbnailClicked"] = value; }
        }

        [DefaultValue(""),
        Category("ClientSide Events"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string OnClientThumbnailLoaded
        {
            get { return (ViewState["OnClientThumbnailLoaded"] == null) ? string.Empty : (string)ViewState["OnClientThumbnailLoaded"]; }
            set { ViewState["OnClientThumbnailLoaded"] = value; }
        }

        /// <summary>
        /// When true the control will try to resgister the css required by this control.
        /// The default is true. When this control is consumed by the HTML Editor, it's set to
        /// false unless an external stylesheet is referenced by the Embedded instance of this control.
        /// This property is useful for control developers and not for use in user code.
        /// </summary>
        [Browsable(false),
        DefaultValue(true)]
        public virtual bool RegisterCSS
        {
            get
            {
                return (bool)(ViewState["RegisterCSS"] ?? true);
            }
            set
            {
                ViewState["RegisterCSS"] = value;
            }
        }

        [DefaultValue(typeof(Color), ""),
        Description("The shaded shadow cast when showing the editors dialog window"),
        TypeConverter(typeof(WebColorConverter)),
        Category("Appearance"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public Color ShaderColor
        {
            get { return (ViewState["ShaderColor"] == null) ? Color.Empty : (Color)ViewState["ShaderColor"]; }
            set { ViewState["ShaderColor"] = value; }
        }

        [DefaultValue(20),
        Description("The shaded shadow's opacity cast when showing the editors dialog window"),
        Category("Appearance"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public int ShaderOpacity
        {
            get { return (ViewState["ShaderOpacity"] == null) ? 20 : (int)ViewState["ShaderOpacity"]; }
            set { ViewState["ShaderOpacity"] = value; }
        }

        /// <summary>
        /// Control ClientID which will activate this control when clicked. 
        /// This can very well be the id of an html element
        /// not necessarily a ServerSide control.
        /// </summary>
        [TypeConverter(typeof(AssociatedHyperLinkConverter)),
        IDReferenceProperty,
        Category("Behavior"),
        Themeable(false),
        DefaultValue(""),
        Description("Target Control"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string TargetControl
        {
            get
            {
                return (ViewState["TargetControl"] == null) ? string.Empty : (string)ViewState["TargetControl"];
            }
            set
            {
                this.ViewState["TargetControl"] = value;
            }
        }

        /// <summary>
        /// The event on which the TargetControl gets to activate and launch the ImagePicker. The default is
        /// click.
        /// </summary>
        [DefaultValue(typeof(ClientEventType), "click"),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public ClientEventType TargetControlActivationOn
        {
            get { return (ViewState["TargetControlActivationOn"] == null) ? ClientEventType.click : (ClientEventType)ViewState["TargetControlActivationOn"]; }
            set { (ViewState["TargetControlActivationOn"]) = value; }
        }

        /// <summary>
        /// Only takes effect if target control is not specified and ManuallyInitialize is false. When this
        /// condition is met, by default an image is used to popup the Image Picker
        /// </summary>
        [Editor(typeof(System.Web.UI.Design.ImageUrlEditor), typeof(UITypeEditor)),
        DefaultValue(""),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string TargetControlImageUrl
        {
            get { return (ViewState["TargetControlImageUrl"] == null) ? string.Empty : (string)ViewState["TargetControlImageUrl"]; }
            set { ViewState["TargetControlImageUrl"] = value; }
        }

        /// <summary>
        /// Gets/Sets the Target image element on which to edit. Use this property when  you plan on making an image element editable.
        /// NOTE: Do not set this property when using the image picker along with the html editor. The editor doesn't require you to set this.
        /// </summary>
        [DefaultValue(""),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string TargetImageElementId
        {
            get { return (string)(ViewState["TargetImageElementId"] ?? string.Empty); }
            set { ViewState["TargetImageElementId"] = value; }
        }

        /// <summary>
        /// The url to a default thumbnail to display in case the original intended image load fails.
        /// </summary>
        [DefaultValue("")]
        [Editor(typeof(System.Web.UI.Design.UrlEditor), typeof(UITypeEditor)),
        NotifyParentProperty(true),
        RefreshProperties(RefreshProperties.Repaint)]
        public string ThumbnailLoadFailureUrl
        {
            get
            {
                return (string)(ViewState["ThumbnailLoadFailureUrl"] ?? string.Empty);
            }
            set
            {
                ViewState["ThumbnailLoadFailureUrl"] = value;
            }
        }

        [DefaultValue(typeof(Unit), "100px")]
        public Unit ThumbnailWidth
        {
            get
            {
                return (Unit)(ViewState["ThumbnailWidth"] ?? new Unit("100px"));
            }
            set
            {
                ViewState["ThumbnailWidth"] = value;
            }
        }

        [DefaultValue(""),
        Category("")]
        public string UpdatePanelProgressId
        {
            get { return (ViewState["UpdatePanelProgressId"] == null) ? string.Empty : (string)ViewState["UpdatePanelProgressId"]; }
            set { ViewState["UpdatePanelProgressId"] = value; }
        }

        [TypeConverter(typeof(WebColorConverter)),
        DefaultValue(typeof(Color), "black")]
        public Color UpdateProgressBackgroundColor
        {
            get
            {
                if (_updateProgressBackgroundColor == Color.Empty)
                    _updateProgressBackgroundColor = Color.Black;
                return _updateProgressBackgroundColor;
            }
            set
            {
                _updateProgressBackgroundColor = value;
            }
        }

        [DefaultValue(20)]
        public int UpdateProgressTransparency
        {
            get
            {
                return _updateProgressTransparency;
            }
            set
            {
                _updateProgressTransparency = value;
            }
        }

        protected override string ClassName
        {
            get
            {
                string cn = (this.CssClass != string.Empty) ? this.CssClass : this.ClientID;
                return cn + " imagepicker";
            }
        }

        protected override string CssKey
        {
            get { return _cssKey; }
        }

        protected override string KeyFormat
        {
            get { return _keyFormat; }
        }

        protected override bool RenderSharedStyles
        {
            get { return true; }
        }

        protected override bool RenderTreeViewStyles
        {
            get { return true; }
        }

        protected override string RootImageSkinFolder
        {
            get
            {
                string url = _rootImageSkinFolder;
                if (IsCustomSkin)
                    url = Helper.GetPhysicalPathByResourceNamespace(url, this);
                return url;
            }
        }

        protected override string RootSkinFolder
        {
            get
            {
                string url = _rootSkinFolder;
                if (IsCustomSkin)
                    url = Helper.GetPhysicalPathByResourceNamespace(url, this);
                return url;
            }
        }

        protected override string RootTemplateSkinFolder
        {
            get
            {
                string url = _rootTemplateSkinFolder;
                if (IsCustomSkin)
                    url = Helper.GetPhysicalPathByResourceNamespace(url, this);
                return url;
            }
        }

        protected override string StartupCssStyleIdentifier
        {
            get { return "ip"; }
        }

        protected override string StyleSheetName
        {
            get { return _styleSheetName; }
        }

        #endregion Properties

        #region Methods

        public override void DataBind()
        {
            this.OnDataBinding(EventArgs.Empty);
            this.EnsureChildControls();
            this.DataBindChildren();
        }

        internal List<CallbackHandler> GetAsyncEvents()
        {
            List<CallbackHandler> handlerList = new List<CallbackHandler>();
            handlerList.Add(new CallbackHandler("insertimage", "ImageDesignerRequest", ImageDesignerLoadingMessage));// image designer
            handlerList.Add(new CallbackHandler("imagebrowser", "ImageBrowserRequest", ImageBrowserLoadingMessage));// image browser
            handlerList.Add(new CallbackHandler("imageupload", "ImageUploadRequest", ImageUploadDesignerLoadingMessage));// image upload
            return handlerList;
        }

        internal virtual void OnBrowseServerRequest(EventArgs e)
        {
            if (BrowseServerRequest != null)
                BrowseServerRequest(this, e);

            HTreeView tv = new HTreeView(ClientID + "_treeview", this.ClassName + " htreeview");
            if (ImageGalleryNode.ChildNodes.Count > 0)
                tv.Nodes.Add(ImageGalleryNode);

            string treeview = tv.Render();
            string templateName = string.Format(RootTemplateSkinFolder,
                     SelectedSkin, "ImageBrowser.html");
            _webResponseData = this.GetTemplateData(templateName).Replace("controlID", ClassName);
            _webResponseData = _webResponseData.Replace("#imgExplorer#", treeview);
        }

        internal virtual void OnImageDesignerRequest(EventArgs e)
        {
            string templateName = string.Format(RootTemplateSkinFolder,
            SelectedSkin, "ImageDesigner.html");
            _webResponseData = this.GetTemplateData(templateName).Replace("controlID", ClassName);
        }

        internal virtual void OnImagePropertiesChanged(ImageEventArgs e)
        {
            if (ImagePropertiesChanged != null)
            {
                ImagePropertiesChanged(this, e);
            }
        }

        internal virtual void OnImageUploaded(UploadedImageFileEventArgs e)
        {
            if (ImageUploaded != null)
            {
                ImageUploaded(this, e);
            }
            _webResponseData = ToJsonString(e);
        }

        internal virtual void OnImageUploadRequest(EventArgs e)
        {
            string templateName = string.Format(RootTemplateSkinFolder,
            SelectedSkin, "ImageFileUpload.html");
            _webResponseData = this.GetTemplateData(templateName).Replace("controlID", ClassName);
        }

        internal virtual void OnSelectedImageChanged(SelectedImageEventArgs e)
        {
            if (SelectedImageChanged != null)
            {
                SelectedImageChanged(this, e);
            }
        }

        internal virtual void RegisterAsyncEvents(ref ScriptControlDescriptor descriptor)
        {
            List<CallbackHandler> handlerList = GetAsyncEvents();
            descriptor.AddScriptProperty("callbacks", ToJsonString(handlerList));
        }

        internal virtual void SetAuthorityDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("authority", Context.Request.Url.GetLeftPart(UriPartial.Authority));
        }

        internal virtual void SetClientSideEventsDescriptor(ref ScriptControlDescriptor descriptor)
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("targetControlClick", OnClientTargetControlAction);
            dic.Add("thumbnailLoaded", OnClientThumbnailLoaded);
            dic.Add("thumbnailclicked", OnClientThumbnailClicked);
            dic.Add("beginWebRequest", OnClientBeginWebRequest);
            dic.Add("endWebRequest", OnClientEndWebRequest);
            dic.Add("okButton", OnClientOkButton);
            dic.Add("cancelButton", OnClientCancelButton);
            foreach (string eventName in dic.Keys)
            {
                string val = dic[eventName];
                if (!string.IsNullOrEmpty(val))
                    descriptor.AddEvent(eventName, val);
            }
        }

        internal virtual void SetDisplayImageDesignerAttributesDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("displayImageDesignerAttributes", DisplayImageDesignerAttributes);
        }

        internal virtual void SetDisplayImageDesignerUrlFieldDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("displayImageDesignerUrlField", DisplayImageDesignerUrlField);
        }

        internal virtual void SetFormElementIdDescriptor(ref ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("formElementId", Page.Form.ClientID);
        }

        internal virtual void SetGlobalClassNameDescriptor(ref ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("className", ClassName);
        }

        internal virtual void SetHttpHandlerUrlDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            string url = string.Format("{0}&width={1}&cq={2}&im={3}",
                Helper.GetEmbeddedWebResourceUrl(typeof(ImagePicker), "tg", false),
                ThumbnailWidth.Value, this.ComposititionQuality, this.InterpolationMode);
            descriptor.AddProperty("httpHandlerUrl", string.Format("{0}&imgurl=", Helper.HashQueryString(url)));
        }

        internal virtual void SetImageBrowserEnabledDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("imageBrowserEnabled", ImageBrowserEnabled);
        }

        internal virtual void SetImageUploadEnabledDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("imageUploadEnabled", ImageUploadEnabled);
        }

        internal virtual void SetImageUploadingMessageDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("imageUploadingMessage", ImageUploadingMessage);
        }

        internal virtual void SetMediaFrameIdDescriptor(ref ScriptControlDescriptor descriptor)
        {
            string id = (EnableMediaFrame) ? EmbeddedMediaFrame.ClientID : string.Empty;
            descriptor.AddProperty("mediaFrameId", id);
        }

        internal virtual void SetPostbackAllFormDataDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("postbackAllFormData", PostbackAllFormData);
        }

        internal virtual void SetShaderColorDescriptor(ref ScriptControlDescriptor descriptor)
        {
            if (ShaderColor != Color.Empty)
                descriptor.AddProperty("shaderColor", ColorTranslator.ToHtml(ShaderColor));
        }

        internal virtual void SetShaderOpacityDescriptor(ref ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("shaderOpacity", ShaderOpacity);
        }

        internal virtual void SetTargetControlDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            string id = string.Empty;
            if (TargetControl != string.Empty)
                id = TargetControl;
            else if (_popperImage != null)
                id = _popperImage.ClientID;
            if (id != string.Empty)
                descriptor.AddProperty("targetControlId", id);
        }

        internal virtual void SetTargetControlEventNameDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("targetControlActivationOn", TargetControlActivationOn.ToString());
        }

        internal virtual void SetTargetImageElementIdDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("targetImageElementId", TargetImageElementId);
        }

        internal virtual void SetThumbnailLoadFailureUrlDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            string url = ThumbnailLoadFailureUrl;
            if (string.IsNullOrEmpty(url))
                url = Helper.GetBackgroundImageUrl(this,
                    string.Format(RootImageSkinFolder, SelectedSkin, "404.gif"));
            descriptor.AddProperty("loadFailureUrl", url ?? string.Empty);
        }

        internal virtual void SetThumbnailWidthDescriptor(ref  ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("thumbnailWidth", ThumbnailWidth.Value);
        }

        internal virtual void SetUniqueIdDescriptor(ref ScriptControlDescriptor descriptor)
        {
            descriptor.AddProperty("uniqueId", this.UniqueID);
        }

        internal virtual void SetUpdatePanelProgressIdDescriptor(ref ScriptControlDescriptor descriptor)
        {
            string id = string.IsNullOrEmpty(UpdatePanelProgressId) ?
                EmbeddedUpdatePanelProgress.ClientID : UpdatePanelProgressId;
            descriptor.AddProperty("updatePanelProgressId", id);
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            // override to disable
            if (this.ID != null)
                writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
            if (this.ClassName != string.Empty)
                writer.AddAttribute("class", ClassName);
            string accessKey = this.AccessKey;
            if (accessKey.Length > 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, accessKey);
            if (!this.Enabled)
                writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
        }

        protected override void CreateChildControls()
        {
            if (!IsBrowserUpLevel)
                this.Controls.Add(new HtmlGenericControl("li"));//make w3c happy with legal ul/li

            if (_cs == null && Page != null)
                _cs = Page.ClientScript;

            CreateFileUploader();
            if ((!IsDesignMode && !IsCallback) && this.Page != null)
                this.Page.Form.Enctype = "multipart/form-data";
        }

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Typps.ImagePicker", this.ClientID);
            if (DesignMode)
                return new ScriptDescriptor[] { descriptor };
            SetGlobalClassNameDescriptor(ref descriptor);
            SetShaderColorDescriptor(ref descriptor);
            SetShaderOpacityDescriptor(ref descriptor);
            SetImageUploadingMessageDescriptor(ref descriptor);
            RegisterAsyncEvents(ref descriptor);
            SetClientSideEventsDescriptor(ref descriptor);
            SetTargetControlDescriptor(ref descriptor);
            SetTargetControlEventNameDescriptor(ref descriptor);
            SetImageBrowserEnabledDescriptor(ref descriptor);
            SetImageUploadEnabledDescriptor(ref descriptor);
            SetUniqueIdDescriptor(ref descriptor);
            SetUpdatePanelProgressIdDescriptor(ref descriptor);
            SetMediaFrameIdDescriptor(ref descriptor);
            SetDisplayImageDesignerAttributesDescriptor(ref descriptor);
            SetDisplayImageDesignerUrlFieldDescriptor(ref descriptor);
            SetTargetImageElementIdDescriptor(ref descriptor);

            SetFormElementIdDescriptor(ref descriptor);
            SetPostbackAllFormDataDescriptor(ref descriptor);
            SetAuthorityDescriptor(ref descriptor);
            SetHttpHandlerUrlDescriptor(ref descriptor);
            SetThumbnailLoadFailureUrlDescriptor(ref descriptor);
            SetThumbnailWidthDescriptor(ref descriptor);
            return new ScriptDescriptor[] { descriptor };
        }

        protected override IEnumerable<ScriptReference> GetScriptReferences()
        {
            if (_cs == null) return null;
            ScriptReference reference1 = new ScriptReference();
            bool isDebugMode = SM.ScriptMode == ScriptMode.Debug;
            string script1 = !isDebugMode ? "ClientScripts.startup.min.js" : "ClientScripts.startup.js";
            string script2 = !isDebugMode ? "ClientScripts.modalpopup.min.js" : "ClientScripts.modalpopup.js";
            string script3 = !isDebugMode ? "ClientScripts.imagepicker.min.js" : "ClientScripts.imagepicker.js";
            reference1.Path = _cs.GetWebResourceUrl(typeof(ResourceDummy),
                Helper.GetManifestResourceName(script1,
                ManifestResourceNamesCache, isDebugMode));

            ScriptReference reference2 = new ScriptReference();
            reference2.Path = _cs.GetWebResourceUrl(typeof(ResourceDummy),
                Helper.GetManifestResourceName(script2,
                ManifestResourceNamesCache, isDebugMode));

            ScriptReference reference3 = new ScriptReference();
            reference3.Path = _cs.GetWebResourceUrl(typeof(ImagePicker),
                Helper.GetManifestResourceName(script3,
                ManifestResourceNamesCache, isDebugMode));

            return new ScriptReference[] { reference1, reference2, reference3 };
        }

        protected override void LoadViewState(object state)
        {
            if (state != null)
            {
                object[] states = (object[])state;
                base.LoadViewState(states[0]);
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            /*
                the second request for this page is being made for the css file stream;
                so in case you see onload executing twice, that is because, the second
                request is a request for css file stream! We prefer to go through the
                page life cycle anyway because we also want to collect user specified
                css properties.
             */
            base.OnLoad(e);
            _cs = Page.ClientScript;
            EnsureChildControls();
            if (!this.DesignMode)
            {
                EnsureScriptManager();
                RaiseCallbackEvent();

                if (IsFileUploading)
                {
                    if (Context.Request.Files.Count > 0)
                    {
                        string argument = Context.Request.Form[Page.postEventArgumentID];
                        FileTransmitInfo fti = FromJson<FileTransmitInfo>(argument);
                        UploadedImageFileEventArgs args = new UploadedImageFileEventArgs(
                            fti.Id, Context.Request.Files[fti.Id]);
                        OnImageUploaded(args);
                    }
                }
                else
                {
                    string postbackData = ExtractPostbackData();
                    if (!string.IsNullOrEmpty(postbackData))
                    {
                        ImageEventArgs args = new ImageEventArgs(postbackData);
                        OnImagePropertiesChanged(args);
                        OnSelectedImageChanged(new SelectedImageEventArgs(args.Src));
                    }
                }
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            if (_enumerateGallery)
                OnBrowseServerRequest(new EventArgs());

            if (IsCallback) return;
            if (!this.DesignMode)
            {
                if (SM == null)
                    throw new HttpException("A ScriptManager control must exist on the current page.");

                SM.RegisterScriptControl(this);
            }
            if (RegisterCSS)
                InitializeCSSRegistration();
        }

        protected override void OnUnload(EventArgs e)
        {
            base.OnUnload(e);
            if (DesignMode) return;
            if (IsFileUploading)
            {
                ResponseWrite(_webResponseData, "text/plain");
            }
            else if (IsCallback)
            {
                if (!string.IsNullOrEmpty(CallbackArgument))
                {
                    ResponseWrite(string.Format("{0}$callbackdata${1}$sep${2}",
                        CallbackArgument, this.ClientID, _webResponseData), "text/plain");
                }
            }
        }

        protected override string QueryStringData(CssFile cssFileToRegister)
        {
            bool formatStyleSheet = SM.ScriptMode == ScriptMode.Debug;
            StringBuilder queryStrings = new StringBuilder(7);
            queryStrings.AppendFormat("&ctl={0}", (int)ControlName.ImagePicker);
            queryStrings.AppendFormat("&s={0}", (int)Skin);
            queryStrings.AppendFormat("&cs={0}", CustomSkin);
            queryStrings.AppendFormat("&csf={0}", CustomSkinFolder);
            queryStrings.AppendFormat("&cc={0}", ClassName);
            queryStrings.AppendFormat("&fcss={0}", formatStyleSheet);
            queryStrings.AppendFormat("&cssfile={0}", cssFileToRegister);

            return queryStrings.ToString();
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (IsCallback) return;
            if (base.DesignMode)
            {
                this.EnsureChildControls();
            }
            else
            {
                if (SM != null && SM.SupportsPartialRendering)
                {
                    SM.RegisterScriptDescriptors(this);
                    string disposeComponent = string.Format(@"$find('{0}').dispose();", this.ClientID);
                    //SM.RegisterDispose(this, disposeComponent);
                }
            }
            base.Render(writer);
        }

        protected override void ResetCollectionsAndStyles()
        {
        }

        protected override object SaveViewState()
        {
            object[] states = new object[1];
            states[0] = base.SaveViewState();
            return states;
        }

        protected override void TrackViewState()
        {
            base.TrackViewState();
        }

        private void CreateFileUploader()
        {
            if (TargetControl == string.Empty && !ManuallyInitialize)
            {
                HtmlGenericControl li = new HtmlGenericControl("li");
                _popperImage = new System.Web.UI.WebControls.Image();
                _popperImage.ID = "icon";
                string url = Helper.GetBackgroundImageUrl(this,
                    string.Format(RootSkinFolder, SelectedSkin, "Images.Shared.img.gif"));

                if (TargetControlImageUrl != string.Empty)
                    url = ResolveClientUrl(TargetControlImageUrl);

                _popperImage.ImageUrl = url;
                _popperImage.Height = Unit.Parse("14px");
                _popperImage.Width = Unit.Parse("14px");
                _popperImage.Style["cursor"] = "pointer";
                _popperImage.EnableViewState = false;
                li.Controls.Add(_popperImage);
                this.Controls.Add(li);
            }
            if (!DesignMode)
            {
                HtmlGenericControl li1 = new HtmlGenericControl("li");
                li1.Style["display"] = "none";
                HiddenField hf = new HiddenField();
                hf.ID = "properties";
                hf.Value = string.Empty; // always reset it. we are collecting the value from the forms collection!
                li1.Controls.Add(hf);
                this.Controls.Add(li1);
            }
            HtmlGenericControl li2 = new HtmlGenericControl("li");
            if (DesignMode)
            {
                li2.Style["display"] = "none";
            }
            this.Controls.Add(li2);
            if (string.IsNullOrEmpty(UpdatePanelProgressId))
                CreateUpdatePanelProgress(li2);
            if (EnableMediaFrame && ImageBrowserEnabled)
                CreateMediaFrame(li2);
        }

        void CreateMediaFrame(Control container)
        {
            EmbeddedMediaFrame.ID = "mediaframe";

            EmbeddedMediaFrame.UpdatePanelProgressId = UpdatePanelProgressId;
            EmbeddedMediaFrame.ManuallyInitialize = true;

            if (Skin != SkinNames.Classic)
                EmbeddedMediaFrame.Skin = Skin;

            if (!string.IsNullOrEmpty(CustomSkinFolder))
                EmbeddedMediaFrame.CustomSkinFolder = CustomSkinFolder;

            if (CustomSkin != "None")
                EmbeddedMediaFrame.CustomSkin = CustomSkin;

            if (PostbackAllFormData)
                EmbeddedMediaFrame.PostbackAllFormData = PostbackAllFormData;

            if (!string.IsNullOrEmpty(ExternalStyleSheet) ||
                !string.IsNullOrEmpty(EmbeddedMediaFrame.ExternalStyleSheet))
                EmbeddedMediaFrame.RegisterCSS = false;

            container.Controls.Add(EmbeddedMediaFrame);
        }

        void CreateUpdatePanelProgress(Control container)
        {
            EmbeddedUpdatePanelProgress.AttachToUpdatePanelEventHandlers = false;

            if (Skin != SkinNames.Classic && EmbeddedUpdatePanelProgress.Skin == SkinNames.Classic)
                EmbeddedUpdatePanelProgress.Skin = Skin;

            if (!string.IsNullOrEmpty(CustomSkinFolder) && string.IsNullOrEmpty(EmbeddedUpdatePanelProgress.CustomSkinFolder))
                EmbeddedUpdatePanelProgress.CustomSkinFolder = CustomSkinFolder;

            if (CustomSkin != "None" && EmbeddedUpdatePanelProgress.CustomSkin == "None")
                EmbeddedUpdatePanelProgress.CustomSkin = CustomSkin;

            if (!string.IsNullOrEmpty(ExternalStyleSheet) ||
               !string.IsNullOrEmpty(EmbeddedUpdatePanelProgress.ExternalStyleSheet))
                EmbeddedUpdatePanelProgress.RegisterCSS = false;

            container.Controls.Add(EmbeddedUpdatePanelProgress);
        }

        private string ExtractPostbackData()
        {
            string key = string.Format("{0}${1}", UniqueID, "properties");
            string value = Context.Request.Form[key];
            if (!string.IsNullOrEmpty(value))
                return value;
            else
                return string.Empty;
        }

        void PopulateMediaItems(HTreeNode treeNode)
        {
            if (EmbeddedMediaFrame == null) return;
            EmbeddedMediaFrame.MediaItems.Add(new MediaItem(treeNode.ToolTip, treeNode.Value, treeNode.Text));
        }

        private void RaiseCallbackEvent()
        {
            if (string.IsNullOrEmpty(CallbackArgument))
                return;
            switch (CallbackArgument)
            {
                case "ImageBrowserRequest":
                    _enumerateGallery = true;
                    break;
                case "ImageDesignerRequest":
                    OnImageDesignerRequest(new EventArgs());
                    break;
                case "ImageUploadRequest":
                    OnImageUploadRequest(new EventArgs());
                    break;
            }
        }

        private void RecurseChildrenNodes(HTreeNode treeNode)
        {
            foreach (HTreeNode tn in treeNode.ChildNodes)
                PopulateMediaItems(tn);
        }

        private void RecursePopulateMediaItems()
        {
            foreach (HTreeNode tn in ImageGalleryNode.ChildNodes)
            {
                PopulateMediaItems(tn);
                RecurseChildrenNodes(tn);
            }
        }

        #endregion Methods
    }
}